package clientcore

import (
	"context"
	"encoding/json"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"gitee.com/huanghua_2017/blade/api"
	"gitee.com/huanghua_2017/blade/state"
	"gitee.com/huanghua_2017/hggutils/jsonrpc"
	"github.com/gorilla/mux"
	"go.uber.org/fx"
)

var DefaultCore *Core

type coreConfig struct {
	filePath     string
	Listen       string
	PACListen    string
	ProxyAddress string
	Index        string
	ServerAddrs  map[string]string
	PAC          string
}

// Core ...
type Core struct {
	state     *state.StatusStruct
	logger    *log.Logger
	Cfg       *coreConfig
	listener  net.Listener
	rpcServer *jsonrpc.RPCServer
	svr       *http.Server
}

// NewCore ...
func NewCore(lc fx.Lifecycle, cfg *coreConfig, lg *log.Logger, st *state.StatusStruct) (*Core, error) {
	c := &Core{
		logger:    lg,
		Cfg:       cfg,
		state:     st,
		rpcServer: jsonrpc.NewServer(),
	}

	//服务器监听
	listener, err := net.Listen("tcp", c.Cfg.Listen)
	c.logger.Printf("Listen %s ：%+v\n", c.Cfg.Listen, err)
	if err != nil {
		return nil, err
	}
	c.listener = listener

	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			c.logger.Print("Starting server.")
			go func() {
				for {
					conn, err := listener.Accept() //开启监听
					if err != nil {
						c.logger.Println("Accept is err!: ", err)
						continue
					}

					go c.readFristRequest(context.Background(), conn)
				}
			}()
			return nil
		},
		OnStop: func(context.Context) error {
			c.logger.Println("Stopping server.")
			return listener.Close()
		},
	})

	return c, nil
}

func BuildCore(cfg *coreConfig, c *Core) {
	var clientapi api.Client
	clientapi = c
	mux := mux.NewRouter()
	c.rpcServer.Register("Blade", clientapi)

	mux.Handle("/rpc/v0", c.rpcServer)
	mux.PathPrefix("/pac").HandlerFunc(c.pacHandler)
	mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof

	c.svr = &http.Server{
		Handler: mux,
		Addr:    c.Cfg.PACListen,
	}
}

func StartClient(logPath, cfgPath string) error {
	newLog := func() *log.Logger {
		fout, _ := os.Create(filepath.Join(logPath, "log-clientcgo.log"))
		logger := log.New(fout, "", log.Lshortfile|log.LstdFlags)
		return logger
	}
	newConfig := func(logger *log.Logger) (*coreConfig, error) {
		data, err := ioutil.ReadFile(cfgPath)
		if err != nil {
			logger.Printf("ReadFile error: %+v\n", err)
			return &coreConfig{}, err
		}
		cfg := &coreConfig{}
		err = json.Unmarshal([]byte(data), cfg)
		cfg.filePath = cfgPath
		return cfg, err
	}

	var c *Core
	app := fx.New(
		fx.Provide(
			newLog,
			newConfig,
			state.NewState,
			NewCore,
		),
		fx.Invoke(BuildCore),
		fx.Populate(&c),
	)
	DefaultCore = c
	startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	if err := app.Start(startCtx); err != nil {
		return err
	}

	ioutil.WriteFile("capi", []byte(c.Cfg.PACListen), 0644)
	defer os.Remove("capi")
	c.logger.Printf("开始监听: %s, %s\n", c.Cfg.Listen, c.Cfg.PACListen)
	return c.svr.ListenAndServe()
}

func (slf *Core) SwitchProxy(index string) {
	if slf.Cfg == nil || slf.Cfg.ServerAddrs[index] == "" {
		return
	}
	slf.Cfg.Index = index
	data, _ := json.MarshalIndent(slf.Cfg, "", "  ")
	ioutil.WriteFile(slf.Cfg.filePath, data, 0644)
}
